﻿#include "FITKMesherDriverGmsh.h"
#include "FITKMeshGenerateProcessorGmsh.h"
#include "gmsh.h"

#include "FITK_Kernel/FITKAppFramework/FITKAppFramework.h"
#include "FITK_Kernel/FITKAppFramework/FITKGlobalData.h"

#include "FITK_Interface/FITKInterfaceMeshGen/FITKMeshGenInterface.h"
#include "FITK_Interface/FITKInterfaceMeshGen/FITKGlobalMeshSizeInfo.h"
#include "FITK_Interface/FITKInterfaceMeshGen/FITKGlobalMeshGenerateAlgorithmInfo.h"
#include "FITK_Interface/FITKInterfaceMeshGen/FITKRegionMeshSizeBox.h"
#include "FITK_Interface/FITKInterfaceMeshGen/FITKRegionMeshSizeCylinder.h"
#include "FITK_Interface/FITKInterfaceMeshGen/FITKRegionMeshSizeSphere.h"

#include "FITK_Kernel/FITKCore/FITKThreadPool.h"
#include "FITK_Kernel/FITKAppFramework/FITKMessage.h"

#include <QFileInfo>
#include <QTimer>
#include <QDir>

namespace Gmsh
{
    FITKMesherDriverGmsh::FITKMesherDriverGmsh()
    {
        //初始化gmsh
        gmsh::initialize();
        
        //创建定时器，通过定时器实时获取gmsh日志
        _logListen = new QTimer();
        //设置1秒
        _logListen->setInterval(1000);
        connect(_logListen, SIGNAL(timeout()), this, SLOT(timerListenLogSolt()));
    }

    FITKMesherDriverGmsh::~FITKMesherDriverGmsh()
    {
        //gmsh完成
        if (0 == gmsh::isInitialized())
        {
            gmsh::clear();
            gmsh::finalize();
        }
        if (_logListen) delete _logListen;
    }

    void FITKMesherDriverGmsh::startMesher(QStringList info /*= QStringList()*/)
    {
        //获取工作路径
        QString workDir = getValue("WorkDir").toString();
        QString inputFile = getValue("Input").toString();
        QDir dir(workDir);
        QFileInfo fileinfo(inputFile);
        if (!dir.exists() || !fileinfo.isFile())return;

        gmsh::clear();
        //获取线程池
        Core::FITKThreadPool* pool = Core::FITKThreadPool::getInstance();
        if (pool == nullptr)return;
        MeshGenerateGmshThread* meshGenerateThread = new MeshGenerateGmshThread();
        meshGenerateThread->_workDir = workDir;
        meshGenerateThread->_input = inputFile;
        meshGenerateThread->_dataObject = this->_dataObject;
        connect(meshGenerateThread, &MeshGenerateGmshThread::meshGenerateStart, this, [=]() {
            //开始监听
            _logListen->start();
            _logIndex = 0;
        });
        connect(meshGenerateThread, &MeshGenerateGmshThread::meshGenerateEnd, this, [=]() {
            //关闭监听
            _logListen->stop();
            this->timerListenLogSolt();
        });
        connect(meshGenerateThread, SIGNAL(taskFinishedSig(FITKThreadTask*)), this, SIGNAL(mesherFinished()));
        pool->execTask(meshGenerateThread);
    }

    void FITKMesherDriverGmsh::stopMesher(QStringList info /*= QStringList()*/)
    {
        //todo
    }

    void FITKMesherDriverGmsh::timerListenLogSolt()
    {
        // 这里是定时器超时时需要执行的代码,用于获取划分日志打印
        std::vector<std::string> log;
        gmsh::logger::get(log);
        for (; this->_logIndex < log.size(); ++_logIndex)
            AppFrame::FITKMessageNormal(QString::fromStdString(log.at(_logIndex)));
    }

    MeshGenerateGmshThread::~MeshGenerateGmshThread()
    {
    }

    void MeshGenerateGmshThread::run()
    {
        //日志开始，并开始监听
        gmsh::logger::start();
        emit this->meshGenerateStart();
        
        //Gmsh获取几何
        bool ok = this->gmshOpenGeometry();
        
        //开始gmsh网格划分
        ok &= this->startGenerate();
        //划分完成关闭监听
        emit this->meshGenerateEnd();
        gmsh::logger::stop();
        //读取网格
        if (ok)
        {
            FITKMeshGenerateProcessorGmsh meshProcess;
            for (QHash<QString, Core::FITKAbstractDataObject*>::iterator iter = _dataObject.begin(); iter != _dataObject.end(); ++iter)
                meshProcess.insertDataObject(iter.key(), iter.value());
            meshProcess.start();
        }
    }

    bool MeshGenerateGmshThread::gmshOpenGeometry()
    {
        QFileInfo fileinfo(_input);
        if (!fileinfo.isFile()) return false;
        //添加几何模型
        gmsh::vectorpair outDimTags;
        gmsh::model::occ::importShapes(_input.toStdString(), outDimTags, true);
        
        return true;
    }

    bool MeshGenerateGmshThread::startGenerate()
    {
        //获取网格算法信息
        Interface::FITKMeshGenInterface* meshGen = Interface::FITKMeshGenInterface::getInstance();
        if (!meshGen)return false;
        Interface::FITKGlobalMeshGenerateAlgorithmInfo* algorithmInfo = meshGen->getGlobalMeshGenerateAlgorithmInfo();
        if (!algorithmInfo) return false;
        //对几何进行处理
        bool ok = this->gmshGeometryProcess();
        //网格区域加密
        ok &= this->gmshRefinedMesh();
        //gmsh网格划分
        ok &= this->gmshMeshAlgorithmConfig();
        
        if (ok)
        {
            //网格划分
            gmsh::model::mesh::generate(algorithmInfo->getMeshGenerateDimension());
            //网格连贯
            //gmsh::model::mesh::removeDuplicateNodes();
            //gmsh::model::mesh::removeDuplicateElements();
        }
        gmsh::write(_workDir.toStdString() + "/Gmsh/meshFile.msh");
        
        return ok;
    }

    bool MeshGenerateGmshThread::gmshGeometryProcess()
    {
        //获取网格算法信息
        Interface::FITKMeshGenInterface* meshGen = Interface::FITKMeshGenInterface::getInstance();
        if (!meshGen)return false;
        Interface::FITKGlobalMeshGenerateAlgorithmInfo* algorithmInfo = meshGen->getGlobalMeshGenerateAlgorithmInfo();
        if (!algorithmInfo) return false;
        //移除几何重复的实体(包含同一位置的实体)
        if (algorithmInfo->getGeometryCoherence())
            gmsh::model::occ::removeAllDuplicates();
        //几何清理
        //gmsh::vectorpair outDimTags;
        //gmsh::model::occ::healShapes(outDimTags, gmsh::vectorpair(), 1e-4, true, true, true, true, true);
        //同步OCC内核与Gmsh模型 
        gmsh::model::occ::synchronize();
        return true;
    }

    bool MeshGenerateGmshThread::gmshRefinedMesh()
    {
        //获取局部加密区域管理器-可以包含多个加密区域
        Interface::FITKMeshGenInterface* meshGen = Interface::FITKMeshGenInterface::getInstance();
        if (!meshGen)return false;
        Interface::FITKRegionMeshSizeManager* regionMeshMgr = meshGen->getRegionMeshSizeMgr();
        if (!regionMeshMgr)return false;
        int count = regionMeshMgr->getDataCount();
        std::vector<double> fieldsList;
        for (int i = 0; i < count; ++i)
        {
            Interface::FITKAbstractRegionMeshSize* regionMesh = regionMeshMgr->getDataByIndex(i);
            if (!regionMesh) continue;
            int tag = -1;
            Interface::FITKAbstractRegionMeshSize::RegionType typeRegion = regionMesh->getRegionType();
            if (typeRegion == Interface::FITKAbstractRegionMeshSize::RegionType::RegionBox)
                tag = this->addGmshMeshFieldBox(regionMesh);
            else if (typeRegion == Interface::FITKAbstractRegionMeshSize::RegionType::RegionCylinder)
                tag = this->addGmshMeshFieldCylinder(regionMesh);
            else if (typeRegion == Interface::FITKAbstractRegionMeshSize::RegionType::RegionSphere)
                tag = this->addGmshMeshFieldSphere(regionMesh);
            if (tag == -1) continue;
            fieldsList.push_back(tag);
        }
        if (fieldsList.empty()) return true;
        //如果是多个加密区域-必须指定背景网格大小是按最大值还是最小值
        std::string fieldType = true ? "Min" : "Max";
        int tag = gmsh::model::mesh::field::add(fieldType);
        gmsh::model::mesh::field::setNumbers(tag, "FieldsList", fieldsList);
        gmsh::model::mesh::field::setAsBackgroundMesh(tag);
        return true;
    }

    bool MeshGenerateGmshThread::gmshMeshAlgorithmConfig()
    {
        //获取全局尺寸大小数据对象和网格算法信息
        Interface::FITKMeshGenInterface* meshGen = Interface::FITKMeshGenInterface::getInstance();
        if (!meshGen)return false;
        Interface::FITKGlobalMeshSizeInfo* sizeInfo = meshGen->getGlobalMeshSizeInfo();
        if (!sizeInfo)return false;
        Interface::FITKGlobalMeshGenerateAlgorithmInfo* algorithmInfo = meshGen->getGlobalMeshGenerateAlgorithmInfo();
        if (!algorithmInfo) return false;
        //获取网格生成配置数据
        int algorithm2D = algorithmInfo->getValueT<int>("2DAlgorithm");
        int algorithm3D = algorithmInfo->getValueT<int>("3DAlgorithm");
        int reAlgorithm2D = algorithmInfo->getValueT<int>("2DReAlgorithm");
        bool reTriMesh = algorithmInfo->getValueT<bool>("ReTriangularMesh");
        int subdivisionAlgorithm = algorithmInfo->getValueT<int>("SubdivisionAlgorithm");
        double smoothStep = algorithmInfo->getValueT<double>("SmoothStep");
        double sizeFactor = sizeInfo->getSizeFactor();
        double sizeMin = sizeInfo->getMinSize();
        double sizeMax = sizeInfo->getMaxSize();
        int eleOrader = algorithmInfo->getValueT<int>("ElementOrder");
        bool useIncElem = algorithmInfo->getValueT<bool>("UseIncElem");
        //错误参数判断
        if (fabsl(sizeMax) < 1e-20) return false;
        //设置网格划分配置到gmsh
        this->algorithm2dDisposition(algorithm2D);
        this->algorithm3dDisposition(algorithm3D);
        this->reAlgorithm2dDisposition(reAlgorithm2D);
        gmsh::option::setNumber("Mesh.RecombineAll", reTriMesh);
        this->subdivisionAlgorithmDisposition(subdivisionAlgorithm);
        gmsh::option::setNumber("Mesh.Smoothing", smoothStep);
        gmsh::option::setNumber("Mesh.MeshSizeFactor", sizeFactor);
        gmsh::option::setNumber("Mesh.MeshSizeMin", sizeMin);
        gmsh::option::setNumber("Mesh.MeshSizeMax", sizeMax);
        gmsh::option::setNumber("Mesh.ElementOrder", eleOrader);
        gmsh::option::setNumber("Mesh.SecondOrderIncomplete", useIncElem);
        return true;
    }

    int MeshGenerateGmshThread::addGmshMeshFieldBox(Interface::FITKAbstractRegionMeshSize* regionMesh)
    {
        //gmsh添加boxField
        Interface::FITKRegionMeshSizeBox* regionBox = dynamic_cast<Interface::FITKRegionMeshSizeBox*>(regionMesh);
        if (!regionBox) return -1;
        //获取第一个点,长度
        double point1[3]{ 0.0 }, length[3]{ 0.0 };
        regionBox->getPoint1(point1);
        regionBox->getLength(length);
        //转化point1为最低点，point2为最高点
        double point2[3]{ point1[0], point1[1], point1[2] };
        length[0] < 0 ? point1[0] = point1[0] + length[0] : point2[0] = point1[0] + length[0];
        length[1] < 0 ? point1[1] = point1[1] + length[1] : point2[1] = point1[1] + length[1];
        length[2] < 0 ? point1[2] = point1[2] + length[2] : point2[2] = point1[2] + length[2];
        //获取内部尺寸，外部尺寸，厚度
        double vIn = regionBox->getValueT<double>("InternalSize"), vOut = regionBox->getValueT<double>("ExternalSize"),
            thickness = regionBox->getValueT<double>("Thickness");
        bool background = regionBox->getValueT<bool>("BackgroundRegion");
        //设置网格场数据
        int tag = gmsh::model::mesh::field::add("Box");
        gmsh::model::mesh::field::setNumber(tag, "VIn", vIn);
        gmsh::model::mesh::field::setNumber(tag, "VOut", vOut);
        gmsh::model::mesh::field::setNumber(tag, "XMin", point1[0]);
        gmsh::model::mesh::field::setNumber(tag, "XMax", point2[0]);
        gmsh::model::mesh::field::setNumber(tag, "YMin", point1[1]);
        gmsh::model::mesh::field::setNumber(tag, "YMax", point2[1]);
        gmsh::model::mesh::field::setNumber(tag, "ZMin", point1[2]);
        gmsh::model::mesh::field::setNumber(tag, "ZMax", point2[2]);
        gmsh::model::mesh::field::setNumber(tag, "Thickness", thickness);
        return tag;
    }

    int MeshGenerateGmshThread::addGmshMeshFieldCylinder(Interface::FITKAbstractRegionMeshSize* regionMesh)
    {
        //gmsh添加CylinderField
        Interface::FITKRegionMeshSizeCylinder* regionCylinder = dynamic_cast<Interface::FITKRegionMeshSizeCylinder*>(regionMesh);
        if (!regionCylinder) return -1;
        //获取点坐标和方向向量
        double point[3]{ 0.0 }, axis[3]{ 0.0 };
        regionCylinder->getLocation(point);
        regionCylinder->getDirection(axis);
        //获取长度
        double length = regionCylinder->getLength();
        //根据方向向量和移动距离，获取中心点的方向分量
        double multiplicative = length / 2 / sqrt((axis[0] * axis[0]) + (axis[1] * axis[1]) + (axis[2] * axis[2]));
        double heftAxis[3]{ axis[0] * multiplicative, axis[1] * multiplicative, axis[2] * multiplicative };
        //获取中心点的坐标
        double center[3]{ point[0] + heftAxis[0], point[1] + heftAxis[1], point[2] + heftAxis[2] };
        //获取半径
        double radius = regionCylinder->getRadius();
        //获取内部尺寸，外部尺寸，厚度
        double vIn = regionCylinder->getValueT<double>("InternalSize"), vOut = regionCylinder->getValueT<double>("ExternalSize"),
            thickness = regionCylinder->getValueT<double>("Thickness");
        bool background = regionCylinder->getValueT<bool>("BackgroundRegion");

        //设置网格场数据
        int tag = gmsh::model::mesh::field::add("Cylinder");
        gmsh::model::mesh::field::setNumber(tag, "VIn", vIn);
        gmsh::model::mesh::field::setNumber(tag, "VOut", vOut);
        gmsh::model::mesh::field::setNumber(tag, "Radius", radius);
        gmsh::model::mesh::field::setNumber(tag, "Length", length);
        gmsh::model::mesh::field::setNumber(tag, "XAxis", heftAxis[0]);
        gmsh::model::mesh::field::setNumber(tag, "XCenter", center[0]);
        gmsh::model::mesh::field::setNumber(tag, "YAxis", heftAxis[1]);
        gmsh::model::mesh::field::setNumber(tag, "YCenter", center[1]);
        gmsh::model::mesh::field::setNumber(tag, "ZAxis", heftAxis[2]);
        gmsh::model::mesh::field::setNumber(tag, "ZCenter", center[2]);
        gmsh::model::mesh::field::setNumber(tag, "Thickness", thickness);
        return tag;
    }
    int MeshGenerateGmshThread::addGmshMeshFieldSphere(Interface::FITKAbstractRegionMeshSize* regionMesh)
    {
        //gmsh添加Sphere
        Interface::FITKRegionMeshSizeSphere* regionSphere = dynamic_cast<Interface::FITKRegionMeshSizeSphere*>(regionMesh);
        if (!regionSphere) return -1;
        //获取球心的坐标
        double center[3]{ 0.0 };
        regionSphere->getLocation(center);
        //获取半径
        double radius = regionSphere->getRadius();
        //获取内部尺寸，外部尺寸，厚度
        double vIn = regionSphere->getValueT<double>("InternalSize"), vOut = regionSphere->getValueT<double>("ExternalSize"),
            thickness = regionSphere->getValueT<double>("Thickness");
        bool background = regionSphere->getValueT<bool>("BackgroundRegion");

        //设置网格场数据
        int tag = gmsh::model::mesh::field::add("Ball");
        gmsh::model::mesh::field::setNumber(tag, "VIn", vIn);
        gmsh::model::mesh::field::setNumber(tag, "VOut", vOut);
        gmsh::model::mesh::field::setNumber(tag, "Radius", radius);
        gmsh::model::mesh::field::setNumber(tag, "XCenter", center[0]);
        gmsh::model::mesh::field::setNumber(tag, "YCenter", center[1]);
        gmsh::model::mesh::field::setNumber(tag, "ZCenter", center[2]);
        gmsh::model::mesh::field::setNumber(tag, "Thickness", thickness);

        return tag;
    }

    void MeshGenerateGmshThread::algorithm2dDisposition(int algorithm2D)
    {
        //2D算法
        Interface::FITKGlobalMeshGenerateAlgorithmInfo::Algorithm2D dispositionType = Interface::FITKGlobalMeshGenerateAlgorithmInfo::Algorithm2D(algorithm2D);
        if (dispositionType == Interface::FITKGlobalMeshGenerateAlgorithmInfo::Algorithm2D::Automatic)
            gmsh::option::setNumber("Mesh.Algorithm", 2);
        else if (dispositionType == Interface::FITKGlobalMeshGenerateAlgorithmInfo::Algorithm2D::MeshAdapt)
            gmsh::option::setNumber("Mesh.Algorithm", 1);
        else if (dispositionType == Interface::FITKGlobalMeshGenerateAlgorithmInfo::Algorithm2D::Delaunay2D)
            gmsh::option::setNumber("Mesh.Algorithm", 5);
        else if (dispositionType == Interface::FITKGlobalMeshGenerateAlgorithmInfo::Algorithm2D::FrontalDelaunay)
            gmsh::option::setNumber("Mesh.Algorithm", 6);
        else if (dispositionType == Interface::FITKGlobalMeshGenerateAlgorithmInfo::Algorithm2D::BAMG)
            gmsh::option::setNumber("Mesh.Algorithm", 7);
        else if (dispositionType == Interface::FITKGlobalMeshGenerateAlgorithmInfo::Algorithm2D::FrontalDelaunayForQuads)
            gmsh::option::setNumber("Mesh.Algorithm", 8);
        else if (dispositionType == Interface::FITKGlobalMeshGenerateAlgorithmInfo::Algorithm2D::PackingOfParallelograms)
            gmsh::option::setNumber("Mesh.Algorithm", 9);
        else
            //默认值
            gmsh::option::setNumber("Mesh.Algorithm", 6);
    }

    void MeshGenerateGmshThread::algorithm3dDisposition(int algorithm3D)
    {
        //3D算法
        Interface::FITKGlobalMeshGenerateAlgorithmInfo::Algorithm3D dispositionType = Interface::FITKGlobalMeshGenerateAlgorithmInfo::Algorithm3D(algorithm3D);
        if (dispositionType == Interface::FITKGlobalMeshGenerateAlgorithmInfo::Algorithm3D::Delaunay3D)
            gmsh::option::setNumber("Mesh.Algorithm3D", 1);
        else if (dispositionType == Interface::FITKGlobalMeshGenerateAlgorithmInfo::Algorithm3D::Frontal)
            gmsh::option::setNumber("Mesh.Algorithm3D", 4);
        else if (dispositionType == Interface::FITKGlobalMeshGenerateAlgorithmInfo::Algorithm3D::HXT)
            gmsh::option::setNumber("Mesh.Algorithm3D", 10);
        else if (dispositionType == Interface::FITKGlobalMeshGenerateAlgorithmInfo::Algorithm3D::MMG3D)
            gmsh::option::setNumber("Mesh.Algorithm3D", 7);
        else
            //默认值
            gmsh::option::setNumber("Mesh.Algorithm3D", 1);
    }

    void MeshGenerateGmshThread::reAlgorithm2dDisposition(int reAlgorithm2D)
    {
        //2D重组算法
        Interface::FITKGlobalMeshGenerateAlgorithmInfo::ReAlgorithm2D dispositionType = Interface::FITKGlobalMeshGenerateAlgorithmInfo::ReAlgorithm2D(reAlgorithm2D);
        if (dispositionType == Interface::FITKGlobalMeshGenerateAlgorithmInfo::ReAlgorithm2D::Simple)
            gmsh::option::setNumber("Mesh.RecombinationAlgorithm", 0);
        else if (dispositionType == Interface::FITKGlobalMeshGenerateAlgorithmInfo::ReAlgorithm2D::Blossom)
            gmsh::option::setNumber("Mesh.RecombinationAlgorithm", 1);
        else if (dispositionType == Interface::FITKGlobalMeshGenerateAlgorithmInfo::ReAlgorithm2D::SimpleFullQuad)
            gmsh::option::setNumber("Mesh.RecombinationAlgorithm", 2);
        else if (dispositionType == Interface::FITKGlobalMeshGenerateAlgorithmInfo::ReAlgorithm2D::BlossomFullQuad)
            gmsh::option::setNumber("Mesh.RecombinationAlgorithm", 3);
        else
            //默认值
            gmsh::option::setNumber("Mesh.RecombinationAlgorithm", 1);
    }

    void MeshGenerateGmshThread::subdivisionAlgorithmDisposition(int subAlgorithm)
    {
        //细分算法
        Interface::FITKGlobalMeshGenerateAlgorithmInfo::SubdivisionAlgorithm dispositionType = Interface::FITKGlobalMeshGenerateAlgorithmInfo::SubdivisionAlgorithm(subAlgorithm);
        if (dispositionType == Interface::FITKGlobalMeshGenerateAlgorithmInfo::SubdivisionAlgorithm::None)
            gmsh::option::setNumber("Mesh.SubdivisionAlgorithm", 0);
        else if (dispositionType == Interface::FITKGlobalMeshGenerateAlgorithmInfo::SubdivisionAlgorithm::AllQuads)
            gmsh::option::setNumber("Mesh.SubdivisionAlgorithm", 1);
        else if (dispositionType == Interface::FITKGlobalMeshGenerateAlgorithmInfo::SubdivisionAlgorithm::AllHexas)
            gmsh::option::setNumber("Mesh.SubdivisionAlgorithm", 2);
        else
            //默认值
            gmsh::option::setNumber("Mesh.SubdivisionAlgorithm", 0);
    }
}



